﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class BaseBlock
{
    public BaseBlock(GameObject obj = null, int posX = -1, int posY = -1)
    {
        this.obj = obj;
        this.pos.x = posX;
        this.pos.y = posY;
    }

    public BaseBlock(GameObject obj, Vector2Int pos)
    {
        this.obj = obj;
        this.pos = pos;
    }

    public GameObject obj;
    public GameObject father;
    public Vector2Int pos;

    private void Update()
    {
        obj.transform.localPosition = new Vector3(ConstOfMap.originPos.x + ConstOfMap.xGap * pos.x,
                                                  ConstOfMap.originPos.y + ConstOfMap.yGap * pos.y,
                                                  (ConstOfMap.originPos.y + ConstOfMap.yGap * pos.y + ConstOfMap.yGap * 0.5f) * 0.1f);
    }

    public bool SetPos(Vector2Int p)
    {
        if (Function.OutOfRange(p))
            return false;
        pos = p;
        Update();
        return true;
    }

    public bool Init(GameObject g, Vector2Int p, GameObject fa)
    {
        if (g == null || Function.OutOfRange(p))
            return false;
        obj = g;
        pos = p;
        father = fa;
        Update();
        return true;
    }

    public abstract void SetFlag(int x);
    public abstract int OnHit();           //0:点击未删除 1:点击删除  2：修复废墟
    public abstract int OnDismantle();     //0:顺利拆毁 1：不可拆毁
    public abstract int OnDestroy();       //0:顺利删除 1:暂时不可删除 2:破坏塔
}

public class HardBlock : BaseBlock
{
    public HardBlock(GameObject obj = null, int posX = -1, int posY = -1) : base(obj, posX, posY)
    {

    }
    public HardBlock(GameObject obj, Vector2Int pos) : base(obj, pos)
    {

    }
    public override int OnHit()
    {
        return 0;
    }
    public override int OnDismantle()
    {
        return OnDestroy();
    }
    public override int OnDestroy()
    {
        if (TerrainManager.Instance.DestroyTerrain(obj))
            return 0;
        return 1;
    }
    public override void SetFlag(int x)
    {
        
    }
}

public class BreakableBlock : HardBlock
{
    public int flag;
    public int hard;
    public BreakableBlock(GameObject obj = null, int posX = -1, int posY = -1, int flag = 1) : base(obj, posX, posY)
    {
        this.flag = flag;
    }
    public BreakableBlock(GameObject obj, Vector2Int pos, int flag = 1) : base(obj, pos)
    {
        this.flag = flag;
    }
    public override void SetFlag(int h)
    {
        flag = h >> 2;
        hard = h & 3;
        Color blockc = obj.GetComponent<SpriteRenderer>().color;
        obj.GetComponent<BodyColor>().ReplaceColor(new Color(blockc.r - 0.18f * hard, blockc.g - 0.18f * hard, blockc.b - 0.18f * hard));
    }
    public int GetCost()
    {
        return 1 << (hard);
    }
    public int GetMoney()
    {
        return hard + 1;
    }
    public override int OnHit()
    {
        if(APController.Instance.CheckDecreaseAPForBreakBlock(GetCost()))
        {
            APController.Instance.TryDecreaseAPForBreakBlock(GetCost());
            ResourceManager.Instance.GainResource(ResourceType.Money, GetMoney());
            Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
            return 1;
        }
        return 0;
    }
    public override int OnDestroy()
    {
        if(base.OnDestroy() == 0)
        {
            EventManager.Instance.OnTriggerEvent(flag);
            return 0;
        }
        return 1;
    }
}

public class UnbreakableBlock : HardBlock
{
    public UnbreakableBlock(GameObject obj = null, int posX = -1, int posY = -1) : base(obj, posX, posY)
    {

    }
    public UnbreakableBlock(GameObject obj, Vector2Int pos) : base(obj, pos)
    {

    }
}

public class OccupiedBlock : BaseBlock
{
    public OccupiedBlock(GameObject obj = null, int posX = -1, int posY = -1) : base(obj, posX, posY)
    {
        
    }
    public OccupiedBlock(GameObject obj, Vector2Int pos) : base(obj, pos)
    {
        
    }
    public override int OnHit()
    {
        return 0;
    }
    public override int OnDestroy()
    {
        if (BuildingManager.Instance.DestroyBuilding(obj))
            return 0;
        return 1;
    }
    public override int OnDismantle()
    {
        return OnDestroy();
    }
    public override void SetFlag(int x)
    {
        
    }
}

public class TowerBlock : OccupiedBlock
{
    public bool isRuin;
    public BuildingType bt;
    public TowerBlock(GameObject obj = null, int posX = -1, int posY = -1) : base(obj, posX, posY)
    {
        OnBuild();
        isRuin = false;
        bt = BuildingType.None;
    }
    public TowerBlock(GameObject obj, Vector2Int pos) : base(obj, pos)
    {
        OnBuild();
        isRuin = false;
        bt = BuildingType.None;
    }
    private void OnBuild()
    {
        Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
    }
    public void SetType(BuildingType bt)
    {
        this.bt = bt;
    }
    public override int OnHit()
    {
        //Debug.Log(obj.name);
        if(!isRuin)
        {
            UpdatePanel panel = UIManager.Instance.GetPanel("UpdatePanel") as UpdatePanel;
            panel.tower = obj.GetComponent<BattleObject>();
            panel.blk = father.GetComponent<Block>();
            UIManager.Instance.PushPanel("UpdatePanel", Camera.main.WorldToScreenPoint(Function.LogicPosToWorldPos(pos)));
            return 0;
        }
        if (APController.Instance.CheckDecreaseAPForRepair(bt))
        {
            APController.Instance.TryDecreaseAPForRepair(bt);
            Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
            obj.GetComponent<BattleObject>().enabled = true;
            obj.GetComponent<BattleObject>().ChangeState(AttackActionState.Cooling);
            obj.GetComponent<HitPoint>().enabled = true;
            obj.GetComponent<HitPoint>().FullHp();
            obj.layer = LayerMask.NameToLayer("Tower");
            SpriteRenderer[] s = obj.GetComponentsInChildren<SpriteRenderer>();
            foreach (SpriteRenderer i in s)
                i.enabled = true;
            Animator animator = obj.GetComponentInChildren<Animator>();
            if (animator != null)
                animator.enabled = true;
            SpriteRenderer root = obj.GetComponent<SpriteRenderer>();
            root.sprite = BuildingManager.Instance.GetNormalSprite(bt);
            root.color = new Color(1.0f, 1.0f, 1.0f);
            isRuin = false;
            return 2;
        }
        return 0;
    }
    public override int OnDestroy()
    {
        if (isRuin)
            return base.OnDestroy();
        isRuin = true;
        obj.GetComponent<BattleObject>().enabled = false;
        HitPoint hp = obj.GetComponent<HitPoint>();
        if (hp != null)
        {
            hp.enabled = false;
            hp.setHP(0);
        }
        obj.layer = LayerMask.NameToLayer("Default");
        Animator animator = obj.GetComponentInChildren<Animator>();
        if (animator != null)
            animator.enabled = false;
        SpriteRenderer[] s = obj.GetComponentsInChildren<SpriteRenderer>();
        foreach (SpriteRenderer i in s)
        {
            if(i.name.Contains("Star") == false)
                i.enabled = false;
        }
            
        SpriteRenderer root = obj.GetComponent<SpriteRenderer>();
        root.sprite = BuildingManager.Instance.GetRuinSprite(bt);
        root.color = new Color(0.7f, 0.7f, 0.7f);
        root.enabled = true;

        return 2;
    }
    public override int OnDismantle()
    {
        Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
        return base.OnDestroy();
    }
}
/*
public class RuinBlock : OccupiedBlock
{
    GameObject repair;
    public BuildingType repairbt;
    public RuinBlock(GameObject obj = null, int posX = -1, int posY = -1, GameObject ruin = null, BuildingType ruinbt = BuildingType.None) : base(obj, posX, posY)
    {
        bt = BuildingType.Ruin;
        repair = ruin;
        repairbt = ruinbt;
    }
    public RuinBlock(GameObject obj, Vector2Int pos, GameObject ruin, BuildingType ruinbt = BuildingType.None) : base(obj, pos)
    {
        bt = BuildingType.Ruin;
        repair = ruin;
        repairbt = ruinbt;
    }
    public void SetRuin(GameObject ruin, BuildingType ruinbt)
    {
        repair = ruin;
        repairbt = ruinbt;
    }
    public GameObject Repair()
    {
        BuildingManager.Instance.DestroyBuilding(obj);
        repair.GetComponent<HitPoint>().setHP(100f);
        repair.GetComponent<BattleObject>().ChangeState(AttackActionState.Cooling);
        repair.SetActive(true);
        return repair;
    }
    public override BlockType OnHit()
    {
        if(MapManager.Instance.PlayerDistance(pos) <= ConstOfMap.operateRange && MapManager.Instance.PlayerDistance(pos) != 0 && APController.Instance.CheckDecreaseAPForBuilding(repairbt))
        {
            Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
            return BlockType.Repair;
        }
        return BlockType.Occupied;
    }
    public override GameObject Destroy()
    {
        BuildingManager.Instance.DestroyBuilding(obj);
        if(repair.activeSelf == false)
            BuildingManager.Instance.DestroyBuilding(repair);
        return null;
    }
    public override bool OnDismantle()
    {
        Player.Instance.waveAction(Function.LogicPosToWorldPos(pos));
        Destroy();
        return true;
    }
}
*/
public class MapBlock : OccupiedBlock
{
    public MapBlock(GameObject obj = null, int posX = -1, int posY = -1) : base(obj, posX, posY)
    {
        
    }
    public MapBlock(GameObject obj, Vector2Int pos) : base(obj, pos)
    {
        
    }
}

public class Block : MonoBehaviour
{
    private BaseBlock block = null;
    public BlockType type { get; private set; } = BlockType.Base;
    public BuildingType buildingType { get; private set; } = BuildingType.None;
    public TerrainType terrainType { get; private set; } = TerrainType.None;
    public Vector2Int pos = ConstOfMap.ERRORPOS;
    
    public bool SetPos(int x, int y)
    {
        if (Function.OutOfRange(x, y))
            return false;
        pos = new Vector2Int(x, y);
        this.gameObject.transform.localPosition = new Vector3(ConstOfMap.originPos.x + ConstOfMap.xGap * pos.x,
                                                              ConstOfMap.originPos.y + ConstOfMap.yGap * pos.y,
                                                              (ConstOfMap.originPos.y + ConstOfMap.yGap * pos.y + ConstOfMap.yGap * 0.5f) * 0.1f);
        if(block != null)
            block.SetPos(pos);
        return true;
    }

    public bool SetPos(Vector2Int p)
    {
        return SetPos(p.x, p.y);
    }

    private bool SetBuilding(BuildingType bt, int flag)
    {
        bool res = false;
        switch (bt)
        {
            //建筑 可升级 不可通过
            case BuildingType.BaseCrossbow:
            case BuildingType.BaseCannon:
            case BuildingType.Shop:
            case BuildingType.Production:
            case BuildingType.DarkFrameCrossbow:
            case BuildingType.Nut:
            case BuildingType.WhiteAngelCrossbow:
            case BuildingType.FireAngelCannon:
            case BuildingType.SoulCannon:
                block = new TowerBlock();
                res = block.Init(BuildingManager.Instance.CreateBuilding(bt), pos, gameObject);
                (block as TowerBlock).SetType(bt);
                block.SetFlag(flag);
                MapManager.Instance.SetBlockCode(pos.x, pos.y, bt, flag);
                break;
            /*
            case BuildingType.Ruin:
                //Debug.Log(result);
                BuildingType ruinbt = (block as TowerBlock).bt;
                block = new RuinBlock();
                res = block.Init(BuildingManager.Instance.CreateBuilding(bt), pos);
                (block as RuinBlock).SetRuin(result, ruinbt);
                block.obj.GetComponent<SpriteRenderer>().sprite = BuildingManager.Instance.GetRuinSprite(ruinbt);
                break;
            */
            //地形 不可升级 可通过
            case BuildingType.Marsh:
            case BuildingType.Heat:
            case BuildingType.Muddy:
                block = new MapBlock();
                res = block.Init(BuildingManager.Instance.CreateBuilding(bt), pos, gameObject);
                block.SetFlag(flag);
                MapManager.Instance.SetBlockCode(pos.x, pos.y, bt, flag);
                break;
            case BuildingType.None:
            case BuildingType.Ruin:
            default:
                break;
        }
        return res;
    }

    private bool SetTerrain(TerrainType tt, int flag = 0)
    {
        bool res = false;
        switch (tt)
        {
            case TerrainType.Breakable:
                block = new BreakableBlock();
                res = block.Init(TerrainManager.Instance.CreateTerrain(tt), pos, gameObject);
                block.SetFlag(flag);
                MapManager.Instance.SetBlockCode(pos.x, pos.y, tt, flag);
                break;
            case TerrainType.Unbreakable:
            case TerrainType.Lava:
                block = new UnbreakableBlock();
                res = block.Init(TerrainManager.Instance.CreateTerrain(tt), pos, gameObject);
                block.SetFlag(flag);
                MapManager.Instance.SetBlockCode(pos.x, pos.y, tt, flag);
                break;
            case TerrainType.None:
            default:
                break;
        }
        return res;
    }

    private bool SetBlock(int flag = 0)
    {
        if (Function.OutOfRange(pos))
            return false;
        Destroy(1);
        switch (type)
        {
            case BlockType.Hard:
                buildingType = BuildingType.None;
                SetTerrain(terrainType, flag);
                break;
            case BlockType.Empty:
                buildingType = BuildingType.None;
                terrainType = TerrainType.None;
                block = null;
                break;
            case BlockType.Occupied:
                terrainType = TerrainType.None;
                SetBuilding(buildingType, flag);
                break;
            case BlockType.Base:
            default:
                break;
        }
        return false;
    }

    public void Clear()
    {
        type = BlockType.Empty;
        MapManager.Instance.SetEmptyCode(pos.x, pos.y);
        SetBlock();
    }

    public bool Init(BuildingType bt, int flag = 0)
    {
        if (Function.OutOfRange(pos))
            return false;
        type = BlockType.Occupied;
        buildingType = bt;
        return SetBlock(flag);
    }

    public bool Init(BuildingType bt, int x, int y, int flag = 0)
    {
        if (SetPos(x, y))
        {
            type = BlockType.Occupied;
            buildingType = bt;
            return SetBlock(flag);
        }
        return false;
    }

    public bool Init(BuildingType bt, Vector2Int pos, int flag = 0)
    {
        if (SetPos(pos))
        {
            type = BlockType.Occupied;
            buildingType = bt;
            return SetBlock(flag);
        }
        return false;
    }

    public bool Init(TerrainType tt, int flag = 0)
    {
        if (Function.OutOfRange(pos))
            return false;
        type = BlockType.Hard;
        terrainType = tt;
        return SetBlock(flag);
    }

    public bool Init(TerrainType tt, int x, int y, int flag = 0)
    {
        if (SetPos(x, y))
        {
            type = BlockType.Hard;
            terrainType = tt;
            return SetBlock(flag);
        }
        return false;
    }

    public bool Init(TerrainType tt, Vector2Int pos, int flag = 0)
    {
        if (SetPos(pos))
        {
            type = BlockType.Hard;
            terrainType = tt;
            return SetBlock(flag);
        }
        return false;
    }

    public bool SetHP(float hp)
    {
        if (block == null)
            return false;
        HitPoint hit = block.obj.GetComponent<HitPoint>();
        if (hit == null)
            return false;
        hit.setHP(hp);
        return true;
    }

    public float GetHP()
    {
        if (block == null)
            return 0;
        HitPoint hit = block.obj.GetComponent<HitPoint>();
        if (hit == null)
            return 0;
        return hit.getCurrentHP();
    }

    public bool SetBuff(List<BuffInfo> bi)
    {
        if (block == null)
            return false;
        BattleObject bo = block.obj.GetComponent<BattleObject>();
        if (bo == null)
            return false;
        foreach(BuffInfo i in bi)
        {
            bo.SetBuff(i);
        }
        return true;
    }

    public List<BuffInfo> GetBuff()
    {
        if (block == null)
            return null;
        BattleObject bo = block.obj.GetComponent<BattleObject>();
        if (bo == null)
            return null;
        return bo.GetAllBuff();
    }

    public void Hit()
    {
        if (block == null)
        {
            return;
        }
        int result = block.OnHit();
        if(result == 1)
        {
            Clear();
            MapManager.Instance.SetBlock(pos.x, pos.y);
            MaskManager.Instance.breakStone(pos);
            MapManager.Instance.UpdateDist();
        }
        else if(result == 2)
        {
            buildingType = (block as TowerBlock).bt;
            MapManager.Instance.DelRuinCode(pos.x, pos.y);
        }
    }

    public void Destroy(int value = 0)
    {
        if (block == null)
            return;
        if(value == 1)
        {
            while (block.OnDestroy() == 2) ;
            return;
        }
        int result = block.OnDestroy();
        if(result == 2)
        {
            MapManager.Instance.SetRuinCode(pos.x, pos.y);
            buildingType = BuildingType.Ruin;
        }
        return;
    }

    public void Dismantle()
    {
        if (block == null)
            return;
        int result = block.OnDismantle();
        //Debug.Log(result);
        if (result == 0)
        {
            Clear();
            
        }
    }

}